<?php

declare(strict_types=1);

namespace app\util;

/**
 * 通用价格计算工具。
 *
 * 用途：
 * - 根据单价、数量、折扣、税率计算订单行金额
 * - 控制舍入方式与精度，返回完整的金额拆解
 */
final class PriceCalculationUtil
{
    public const DISCOUNT_TYPE_AMOUNT = 'amount';
    public const DISCOUNT_TYPE_PERCENT = 'percent';

    /**
     * 计算含折扣与税费的金额明细。
     *
     * @param float $unitPrice 单价（含税/未税由业务自定）
     * @param int   $quantity 数量，必须大于 0
     * @param array{
     *     discount?: float,
     *     discount_type?: self::DISCOUNT_*,
     *     max_discount?: float|null,
     *     tax_rate?: float,
     *     scale?: int,
     *     rounding?: int
     * } $options
     *
     * @return array{
     *     unit_price: float,
     *     quantity: int,
     *     base_amount: float,
     *     discount: float,
     *     subtotal: float,
     *     tax: float,
     *     total: float
     * }
     */
    public static function calculate(
        float $unitPrice,
        int $quantity,
        array $options = []
    ): array {
        if ($quantity <= 0) {
            throw new \InvalidArgumentException('数量必须大于 0');
        }

        $scale = self::resolveScale($options['scale'] ?? 2);
        $roundingMode = $options['rounding'] ?? PHP_ROUND_HALF_UP;

        $baseAmount = self::roundAmount($unitPrice * $quantity, $scale, $roundingMode);

        $discount = self::calculateDiscount(
            $baseAmount,
            $options['discount'] ?? 0.0,
            (string) ($options['discount_type'] ?? self::DISCOUNT_TYPE_AMOUNT),
            $options['max_discount'] ?? null,
            $scale,
            $roundingMode
        );

        $subtotal = max(0.0, self::roundAmount($baseAmount - $discount, $scale, $roundingMode));
        $tax = self::calculateTax($subtotal, $options['tax_rate'] ?? 0.0, $scale, $roundingMode);
        $total = self::roundAmount($subtotal + $tax, $scale, $roundingMode);

        return [
            'unit_price' => $unitPrice,
            'quantity' => $quantity,
            'base_amount' => $baseAmount,
            'discount' => $discount,
            'subtotal' => $subtotal,
            'tax' => $tax,
            'total' => $total,
        ];
    }

    /**
     * 计算折扣金额。
     */
    private static function calculateDiscount(
        float $baseAmount,
        float $discount,
        string $discountType,
        ?float $maxDiscount,
        int $scale,
        int $roundingMode
    ): float {
        if ($discount <= 0.0 || $baseAmount <= 0.0) {
            return 0.0;
        }

        $calculated = match ($discountType) {
            self::DISCOUNT_TYPE_PERCENT => $baseAmount * min($discount, 100.0) / 100,
            default => $discount,
        };

        if ($maxDiscount !== null && $maxDiscount >= 0) {
            $calculated = min($calculated, $maxDiscount);
        }

        $calculated = min($calculated, $baseAmount);

        return self::roundAmount($calculated, $scale, $roundingMode);
    }

    /**
     * 计算税额。
     */
    private static function calculateTax(
        float $amount,
        float $taxRate,
        int $scale,
        int $roundingMode
    ): float {
        if ($amount <= 0.0 || $taxRate <= 0.0) {
            return 0.0;
        }

        return self::roundAmount($amount * $taxRate, $scale, $roundingMode);
    }

    /**
     * 执行金额舍入。
     */
    public static function roundAmount(float $value, int $scale, int $mode = PHP_ROUND_HALF_UP): float
    {
        return round($value, $scale, $mode);
    }

    private static function resolveScale(int $scale): int
    {
        if ($scale < 0) {
            return 0;
        }

        if ($scale > 6) {
            return 6;
        }

        return $scale;
    }
}
